#!/bin/bash
# ======================================================================
# MySQL 一键自动化部署脚本
# 版本: 2.0.0
# 作者: Wangzhenye
# Email: Wangzhenye2019@gmail.com
# 最后更新: 2025-11-23
#
# 功能概述:
# 1. 操作系统环境检测与优化
#    - 系统参数调优
#    - 内核参数优化
#    - 透明大页(THP)配置
#    - HugePages 内存优化
# 2. MySQL 自动化部署
#    - 自动下载/使用本地安装包
#    - 多版本支持 (5.7.x / 8.0.x)
#    - 自动适配 glibc 版本
# 3. 安全加固
#    - 默认安全配置
#    - 自动生成强密码
#    - 最小权限原则配置
#
# 使用说明:
# 1. 需要 root 权限执行
# 2. 支持离线安装（需提前下载安装包到本地）
# 3. 详细日志输出到 /var/log/mysql_install/
# ======================================================================

# ======================================================================
#                           配置参数区
# 说明：以下参数可根据实际环境需求进行调整
# ======================================================================

# ------------------------- 日志配置 -------------------------
LOG_DIR="/var/log/mysql_install"
LOG_FILE="${LOG_DIR}/mysql_install_$(date +%Y%m%d_%H%M%S).log"

# ----------------------- MySQL 版本配置 ----------------------
MYSQL_MAJOR="8.0"         # 主版本号，支持 5.7 或 8.0
MYSQL_FULL="8.0.43"       # 完整版本号，例如：8.0.43、5.7.44
MYSQL_ROOT_PASSWORD="$(openssl rand -base64 24 | tr -d '/+=' | head -c 24)!@#"  # 自动生成强密码

# ----------------------- 安装路径配置 -----------------------
INSTALL_DIR="/usr/local/mysql"    # MySQL 安装目录
DATA_DIR="/data/mysql"           # 数据文件目录
PORT=3306                        # 服务监听端口

# ----------------------- 内存优化配置 -----------------------
# InnoDB 缓冲池大小（MB）
# 0 表示自动计算（总内存的75%用于InnoDB缓冲池）
INNODB_BUFFER_POOL_SIZE=0

# HugePages 配置
# 0: 禁用 1: 自动计算 2: 手动指定页数
ENABLE_HUGEPAGE=1
# 当 ENABLE_HUGEPAGE=2 时，指定 HugePages 页数
MANUAL_HUGEPAGE_PAGES=0

# --------------------- 本地软件包配置 -----------------------
# 本地 MySQL 安装包存放目录（优先从此目录查找安装包）
LOCAL_PKG_DIR="/soft/"

# MySQL 下载镜像站列表（按优先级排序）
PKG_URLS=(
  "http://mirrors.ustc.edu.cn/mysql-ftp/Downloads/MySQL-${MYSQL_MAJOR}/"
  "https://mirrors.tuna.tsinghua.edu.cn/mysql/downloads/MySQL-${MYSQL_MAJOR}/"
  "https://mirrors.aliyun.com/mysql/MySQL-${MYSQL_MAJOR}/"
  "https://cdn.mysql.com/Downloads/MySQL-${MYSQL_MAJOR}/"
)

# ======================================================================
#                           函数定义区
# ======================================================================

# 函数: 日志记录
# 参数:
#   $1 - 日志级别 (info/warn/error/debug)
#   $2 - 日志内容
# 返回值: 无
log() {
    local level=$1
    local message="[$(date '+%Y-%m-%d %H:%M:%S')] [${level^^}] ${*:2}"
    
    # 输出到控制台
    case ${level,,} in
        error) echo -e "\033[1;31m${message}\033[0m" ;;
        warn)  echo -e "\033[1;33m${message}\033[0m" ;;
        info)  echo -e "\033[1;32m${message}\033[0m" ;;
        *)     echo "${message}" ;;
    esac
    
    # 输出到日志文件
    mkdir -p "$(dirname "${LOG_FILE}")"
    echo "${message}" >> "${LOG_FILE}"
}

# 命令执行函数，带日志记录
exec_cmd() {
    local cmd="$*"
    log "INFO" "执行命令: ${cmd}"
    
    # 执行命令并捕获输出和返回码
    local output
    local status
    
    # 使用临时文件存储命令输出
    local temp_file=$(mktemp)
    
    # 执行命令
    eval "${cmd}" > "${temp_file}" 2>&1
    status=$?
    
    # 读取命令输出
    output=$(<"${temp_file}")
    rm -f "${temp_file}"
    
    # 记录命令输出
    if [ -n "${output}" ]; then
        while IFS= read -r line; do
            log "DEBUG" "  ${line}"
        done <<< "${output}"
    fi
    
    # 检查命令执行状态
    if [ ${status} -ne 0 ]; then
        log "ERROR" "命令执行失败 [${status}]: ${cmd}"
        return ${status}
    fi
    
    return 0
}

# 安装基础工具包
install_common_packages() {
  log "INFO" "开始安装常用工具包..."
  # 定义常用包列表
  local common_packages=(
    wget curl vim-enhanced net-tools unzip zip lsof \
    htop iotop iftop sysstat telnet nc nmap bind-utils \
    bash-completion bash-completion-extras yum-utils \
    lrzsz screen tmux tree psmisc
  )
  
  # 检查并安装缺失的包
  local to_install=()
  for pkg in "${common_packages[@]}"; do
    if ! rpm -q "$pkg" &>/dev/null; then
      to_install+=("$pkg")
    fi
  done
  
  if [ ${#to_install[@]} -eq 0 ]; then
    log "INFO" "所有需要的包已经安装"
    return 0
  fi
  
  if [ ${#to_install[@]} -gt 0 ]; then
    log "INFO" "正在安装以下工具包: ${to_install[*]}"
    exec_cmd "yum -y install ${to_install[*]}" || {
      log "WARN" "部分包安装失败，继续执行..."
      return 1
    }
  fi
  
  log "INFO" "常用工具包安装完成"
  return 0
}

# 自动配置yum源，支持本地优先，网络备选
config_yum_repo() {
  # 仅适用centos/rhel
  if ! command -v yum &>/dev/null; then
    echo "[INFO] 非yum系统，跳过yum源配置"
    return 1
  fi

  # 1. 首先尝试使用本地yum源
  if [ -d "/mnt/cdrom" ] && [ -f "/mnt/cdrom/.discinfo" ]; then
    echo "[INFO] 检测到本地yum源，优先使用本地源"
    cat > /etc/yum.repos.d/local.repo <<-'EOF'
[local-media]
name=CentOS-$releasever - Media
baseurl=file:///mnt/cdrom/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-$releasever
EOF
    # 禁用其他源
    mv /etc/yum.repos.d/CentOS-*.repo /tmp/ 2>/dev/null || true
    yum clean all
    yum makecache
    
    # 测试yum源是否可用
    if yum -y install wget &>/dev/null; then
      echo "[INFO] 本地yum源配置成功"
      return 0
    fi
    echo "[WARN] 本地yum源不可用，尝试网络源"
  fi

  # 2. 本地源不可用，配置网络源
  echo "[INFO] 配置网络yum源"
  # 检测系统版本
  if [ -f /etc/redhat-release ]; then
    OSVER=$(grep -oE '[0-9]+' /etc/redhat-release | head -1)
  elif [ -f /etc/os-release ]; then
    OSVER=$(grep -oP 'VERSION_ID="\K[0-9]+' /etc/os-release)
  else
    OSVER=7  # 默认使用7
  fi

  # 备份原有repo文件
  mkdir -p /etc/yum.repos.d/backup
  mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/backup/ 2>/dev/null || true

  # 配置阿里云yum源
  if [ "$OSVER" -eq 7 ]; then
    wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
  elif [ "$OSVER" -eq 8 ]; then
    wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo
  else
    wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
  fi

  # 安装epel源
  yum install -y epel-release
  
  # 清理并重建缓存
  yum clean all
  yum makecache
  
  # 测试yum源是否可用
  if yum -y install wget &>/dev/null; then
    echo "[INFO] 网络yum源配置成功"
    return 0
  else
    echo "[ERROR] yum源配置失败，请检查网络或手动配置"
    return 1
  fi
}

# 按内存自动调整 vm.swappiness
set_swappiness() {
  MEM_TOTAL_GB=$(awk '/MemTotal/ {printf "%.0f", $2/1024/1024}' /proc/meminfo)
  if [ "$MEM_TOTAL_GB" -ge 16 ]; then
    SWAPPINESS=1
  elif [ "$MEM_TOTAL_GB" -ge 8 ]; then
    SWAPPINESS=5
  else
    SWAPPINESS=10
  fi
  sysctl -w vm.swappiness=$SWAPPINESS
  if grep -q '^vm.swappiness' /etc/sysctl.conf; then
    sed -i 's/^vm.swappiness=.*/vm.swappiness='$SWAPPINESS'/' /etc/sysctl.conf
  else
    echo "vm.swappiness=$SWAPPINESS" >> /etc/sysctl.conf
  fi
  echo "[INFO] 检测到内存 ${MEM_TOTAL_GB}G，已设置 vm.swappiness=$SWAPPINESS"
}

# 关闭NUMA功能，适配Linux 6/7/8
close_numa() {
  # 关闭grub中的numa=on，添加numa=off
  if grep -q 'numa=on' /proc/cmdline || ! grep -q 'numa=off' /proc/cmdline; then
    if [ -f /etc/default/grub ]; then
      # RHEL7/8
      sed -i '/^GRUB_CMDLINE_LINUX=/ s/"$/ numa=off"/' /etc/default/grub
      if command -v grub2-mkconfig &>/dev/null; then
        grub2-mkconfig -o /boot/grub2/grub.cfg
      fi
    elif [ -f /boot/grub/grub.conf ]; then
      # RHEL6
      sed -i '/^kernel/ s/$/ numa=off/' /boot/grub/grub.conf
    fi
    echo "[INFO] 已设置 grub 启动参数 numa=off（重启生效）"
  else
    echo "[INFO] NUMA 已关闭或未启用，无需修改"
  fi
  # 检查numactl
  if command -v numactl &>/dev/null; then
    ln -sf /bin/true /usr/bin/numactl
    ln -sf /bin/true /usr/bin/numactl64 2>/dev/null || true
    echo "[INFO] 已软链接屏蔽 numactl/numactl64"
  fi
}

# 关闭防火墙和SELinux
close_firewalld_selinux() {
  # 关闭防火墙
  if systemctl is-active firewalld &>/dev/null; then
    systemctl stop firewalld
    systemctl disable firewalld
    echo "[INFO] firewalld 已关闭并禁用自启动"
  fi
  if systemctl is-active ufw &>/dev/null; then
    systemctl stop ufw
    systemctl disable ufw
    echo "[INFO] ufw 已关闭并禁用自启动"
  fi
  # 关闭SELinux
  if [ -f /etc/selinux/config ]; then
    sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
    setenforce 0 2>/dev/null
    echo "[INFO] SELinux 已设置为 disabled"
  fi
}

# 检查 limits.conf 和 90-nproc.conf
check_limits() {
  LIMITS_CONF=/etc/security/limits.conf
  NPROC_CONF=/etc/security/limits.d/90-nproc.conf
  NEED_UPDATE=0
  for line in \
    '* soft nproc 65535' \
    '* hard nproc 65535' \
    '* soft nofile 65535' \
    '* hard nofile 65535' \
    '* soft stack 65535' \
    '* hard stack 65535'
  do
    grep -qF "$line" $LIMITS_CONF || NEED_UPDATE=1
  done
  if [ $NEED_UPDATE -eq 1 ]; then
    cat <<EOF >>$LIMITS_CONF
* soft nproc 65535
* hard nproc 65535
* soft nofile 65535
* hard nofile 65535
* soft stack 65535
* hard stack 65535
EOF
    echo "[INFO] 已追加资源限制到 $LIMITS_CONF，重启生效"
  else
    echo "[INFO] $LIMITS_CONF 已包含资源限制配置"
  fi
  if ! grep -Fxq "* - nproc 65535" $NPROC_CONF 2>/dev/null; then
    echo "* - nproc 65535" > $NPROC_CONF
    echo "[INFO] 已写入 $NPROC_CONF"
  else
    echo "[INFO] $NPROC_CONF 内容已符合要求，无需修改"
  fi
}

# 检查 LANG 环境变量
check_lang() {
  if [ "$LANG" != "en_US.utf-8" ] && [ "$LANG" != "en_US.UTF-8" ]; then
    export LANG=en_US.utf-8
    grep -q 'export LANG=en_US.utf-8' ~/.bash_profile 2>/dev/null || echo 'export LANG=en_US.utf-8' >> ~/.bash_profile
    echo "[INFO] 已设置 LANG=en_US.utf-8，并写入 ~/.bash_profile"
  else
    echo "[INFO] 当前 LANG=$LANG，无需修改"
  fi
}

# 检查 /etc/hosts 是否包含指定 IP 和主机名
check_hosts() {
  HOST_IP="127.0.0.1"
  HOST_NAME=$(hostname)
  if ! grep -q "^127.0.0.1.*localhost" /etc/hosts; then
    echo "127.0.0.1 localhost" >> /etc/hosts
    echo "[INFO] 已添加 localhost 到 /etc/hosts"
  fi
  if ! grep -q "^::1.*localhost" /etc/hosts; then
    echo "::1 localhost" >> /etc/hosts
    echo "[INFO] 已添加 IPv6 localhost 到 /etc/hosts"
  fi
  if ! grep -q "^$HOST_IP[[:space:]]+$HOST_NAME(\s|
|$)" /etc/hosts; then
    echo "$HOST_IP $HOST_NAME" >> /etc/hosts
    echo "[INFO] 已将 $HOST_IP $HOST_NAME 添加到 /etc/hosts"
  else
    echo "[INFO] /etc/hosts 已包含 $HOST_IP $HOST_NAME"
  fi
}

check_os() {
  if [ -f /etc/redhat-release ]; then
    OS=centos
    # 获取CentOS主版本号
    OS_VERSION=$(grep -oE '[0-9]+' /etc/redhat-release | head -1)
  elif [ -f /etc/lsb-release ] || [ -f /etc/debian_version ]; then
    OS=ubuntu
    # 获取Ubuntu版本号
    if [ -f /etc/lsb-release ]; then
      . /etc/lsb-release
      OS_VERSION=${DISTRIB_RELEASE%%.*}
    else
      OS_VERSION=$(cat /etc/debian_version | cut -d. -f1)
    fi
  else
    echo "[ERROR] 不支持的操作系统，仅支持 CentOS/Ubuntu/Debian"
    exit 1
  fi
  echo "[INFO] 检测到操作系统: $OS $OS_VERSION"
}

# 计算推荐的内存参数
calculate_memory_params() {
  # 获取总内存（MB）
  TOTAL_MEM_MB=$(free -m | awk '/^Mem:/{print $2}')
  
  # 计算InnoDB缓冲池大小（总内存的75%或用户指定值）
  if [ $INNODB_BUFFER_POOL_SIZE -eq 0 ]; then
    INNODB_BUFFER_POOL_SIZE=$(($TOTAL_MEM_MB * 75 / 100))
    # 限制最小值为128M
    [ $INNODB_BUFFER_POOL_SIZE -lt 128 ] && INNODB_BUFFER_POOL_SIZE=128
    echo "[INFO] 自动计算 InnoDB 缓冲池大小: ${INNODB_BUFFER_POOL_SIZE}M"
  fi
  
  # 计算推荐的HugePage页数（每页2MB）
  if [ $ENABLE_HUGEPAGE -eq 1 ]; then
    # 推荐值：缓冲池大小 / 2MB * 1.1（预留10%余量）
    HUGEPAGE_PAGES=$(($INNODB_BUFFER_POOL_SIZE / 2 * 11 / 10))
    # 确保至少有4个页（8MB）
    [ $HUGEPAGE_PAGES -lt 4 ] && HUGEPAGE_PAGES=4
    echo "[INFO] 自动计算 HugePage 页数: $HUGEPAGE_PAGES (${HUGEPAGE_PAGES} * 2MB = $(($HUGEPAGE_PAGES * 2))MB)"
  elif [ $ENABLE_HUGEPAGE -eq 2 ]; then
    HUGEPAGE_PAGES=$MANUAL_HUGEPAGE_PAGES
    [ $HUGEPAGE_PAGES -lt 1 ] && HUGEPAGE_PAGES=4
    echo "[INFO] 使用手动指定的 HugePage 页数: $HUGEPAGE_PAGES (${HUGEPAGE_PAGES} * 2MB = $(($HUGEPAGE_PAGES * 2))MB)"
  else
    HUGEPAGE_PAGES=0
    echo "[INFO] 已禁用 HugePage"
  fi
}

# 配置透明大页(THP)
configure_thp() {
  echo "[INFO] 配置透明大页(THP)..."
  
  # 检查THP当前状态
  if [ -f /sys/kernel/mm/transparent_hugepage/enabled ]; then
    THP_STATUS=$(cat /sys/kernel/mm/transparent_hugepage/enabled | awk -F'[][]' '{print $2}')
    echo "[INFO] 当前透明大页(THP)状态: $THP_STATUS"
  else
    echo "[WARN] 未检测到透明大页(THP)支持，跳过配置"
    return
  fi
  
  # 禁用THP
  if [ -f /sys/kernel/mm/transparent_hugepage/defrag ]; then
    echo never > /sys/kernel/mm/transparent_hugepage/defrag
    echo never > /sys/kernel/mm/transparent_hugepage/enabled
    echo "[INFO] 已临时禁用透明大页(THP)"
  fi
  
  # 配置启动时禁用THP
  if [ -f /etc/rc.local ]; then
    if ! grep -q 'transparent_hugepage' /etc/rc.local; then
      sed -i '/^exit 0/i\n# Disable Transparent Huge Pages (THP)
if [ -d /sys/kernel/mm/transparent_hugepage ]; then\
    echo never > /sys/kernel/mm/transparent_hugepage/enabled\
    echo never > /sys/kernel/mm/transparent_hugepage/defrag\
fi\n' /etc/rc.local
      chmod +x /etc/rc.local
      echo "[INFO] 已配置开机自动禁用透明大页(THP)"
    fi
  fi
  
  # 对于systemd系统
  if [ -d /etc/systemd/system ]; then
    cat > /etc/systemd/system/disable-thp.service <<EOF
[Unit]
Description=Disable Transparent Huge Pages (THP)
After=syslog.target

[Service]
Type=oneshot
ExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/enabled'
ExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/defrag'

[Install]
WantedBy=multi-user.target
EOF
    systemctl daemon-reload
    systemctl enable disable-thp.service
    systemctl start disable-thp.service
    echo "[INFO] 已创建并启用 disable-thp.service"
  fi
}

# 配置HugePage
configure_hugepages() {
  if [ $HUGEPAGE_PAGES -eq 0 ]; then
    echo "[INFO] 跳过 HugePage 配置"
    return
  fi
  
  echo "[INFO] 配置 HugePages..."
  
  # 检查是否支持HugePage
  if ! grep -q 'Huge' /proc/meminfo; then
    echo "[WARN] 当前内核不支持HugePage，跳过配置"
    return
  fi
  
  # 设置HugePages
  echo $HUGEPAGE_PAGES > /proc/sys/vm/nr_hugepages
  
  # 永久生效
  if [ -f /etc/sysctl.conf ]; then
    if grep -q '^vm.nr_hugepages' /etc/sysctl.conf; then
      sed -i "s/^vm\.nr_hugepages.*/vm.nr_hugepages = $HUGEPAGE_PAGES/" /etc/sysctl.conf
    else
      echo "vm.nr_hugepages = $HUGEPAGE_PAGES" >> /etc/sysctl.conf
    fi
    sysctl -p
  fi
  
  # 配置limits.conf
  if [ -f /etc/security/limits.conf ]; then
    if ! grep -q '^\*.*memlock' /etc/security/limits.conf; then
      echo -e "\n# For MySQL HugePages\n* soft memlock unlimited\n* hard memlock unlimited" >> /etc/security/limits.conf
      echo "[INFO] 已配置 memlock 限制"
    fi
  fi
  
  # 验证HugePage配置
  HUGE_TOTAL=$(grep '^HugePages_Total:' /proc/meminfo | awk '{print $2}')
  HUGE_FREE=$(grep '^HugePages_Free:' /proc/meminfo | awk '{print $2}')
  HUGE_SIZE=$(grep '^Hugepagesize:' /proc/meminfo | awk '{print $2}')
  echo "[INFO] HugePage 配置完成: ${HUGE_TOTAL}页 x ${HUGE_SIZE}KB = $((HUGE_TOTAL * HUGE_SIZE / 1024))MB (空闲: $((HUGE_FREE * HUGE_SIZE / 1024))MB)"
}

install_deps() {
  echo "[INFO] 安装依赖包..."
  if [ "$OS" = "centos" ]; then
    yum install -y wget lsof net-tools libaio numactl
  elif [ "$OS" = "ubuntu" ]; then
    apt-get update && apt-get install -y wget lsof net-tools libaio1 numactl
  fi
}

# 获取系统glibc版本
get_glibc_version() {
  if [ -x "$(command -v ldd)" ]; then
    ldd --version | awk '/ldd/ {print $NF; exit}'
  else
    # 如果没有ldd命令，尝试其他方法
    if [ -f /usr/lib64/libc.so.6 ]; then
      /usr/lib64/libc.so.6 | awk '/GNU C Library/ {print $NF; exit}'
    else
      echo "2.17"  # 默认返回一个较旧的版本
    fi
  fi
}

# 检查本地是否存在MySQL安装包
check_local_mysql_package() {
  local version=$1
  local glibc_ver=$2
  local pkg_name=""
  
  # 定义可能的安装包名称模式
  local patterns=(
    "mysql-${version}-linux-glibc${glibc_ver/./}-x86_64.tar.xz"
    "mysql-${version}-linux-glibc${glibc_ver/./}-x86_64.tar.gz"
    "mysql-${version}-linux-glibc2.12-x86_64.tar.xz"
    "mysql-${version}-linux-glibc2.12-x86_64.tar.gz"
    "mysql-${version}-linux-glibc2.17-x86_64.tar.xz"
    "mysql-${version}-linux-glibc2.17-x86_64.tar.gz"
  )
  
  # 检查当前目录和常见下载目录
  local search_paths=(
    "./" "/tmp/" "$HOME/" "/usr/local/src/" "/opt/"
  )
  
  echo "[INFO] 正在检查本地MySQL ${version} 安装包..."
  
  for path in "${search_paths[@]}"; do
    [ ! -d "$path" ] && continue
    
    for pattern in "${patterns[@]}"; do
      local pkg_path=$(find "$path" -name "$pattern" -type f -print -quit 2>/dev/null)
      if [ -n "$pkg_path" ]; then
        echo "[INFO] 找到本地安装包: $pkg_path"
        echo "$pkg_path"
        return 0
      fi
    done
  done
  
  echo "[INFO] 未找到匹配的本地MySQL ${version} 安装包"
  return 1
}

# 根据glibc版本获取对应的MySQL包名
get_mysql_package_name() {
    local version=$1
    local glibc_ver=$2
    
    # 确定glibc后缀
    local glibc_suffix="2.12"
    if [ "$(echo "$glibc_ver >= 2.28" | bc -l 2>/dev/null)" = "1" ]; then
        glibc_suffix="2.28"
    elif [ "$(echo "$glibc_ver >= 2.17" | bc -l 2>/dev/null)" = "1" ]; then
        glibc_suffix="2.17"
    fi
    
    # 构建包名
    if [ "$version" = "5.7" ]; then
        echo "mysql-${MYSQL_FULL}-linux-glibc${glibc_suffix}-x86_64.tar.gz"
    else
        echo "mysql-${MYSQL_FULL}-linux-glibc${glibc_suffix}-x86_64-minimal.tar.xz"
# 检查本地安装包
check_local_package() {
  local pkg_pattern=$1
  local target_dir=$2
  
  # 检查本地包目录
  if [ -d "${LOCAL_PKG_DIR}" ]; then
    log "INFO" "检查本地包目录: ${LOCAL_PKG_DIR}"
    local pkg=$(find "${LOCAL_PKG_DIR}" -type f -name "${pkg_pattern}*.tar.xz" -o -name "${pkg_pattern}*.tar.gz" | sort -V | tail -1)
    if [ -n "$pkg" ]; then
      log "INFO" "在本地目录找到安装包: $pkg"
      cp "$pkg" "${target_dir}/"
      return 0
    fi
  fi
  
  # 检查当前目录
  log "INFO" "在当前目录查找安装包..."
  local pkg=$(find . -maxdepth 1 -type f -name "${pkg_pattern}*.tar.xz" -o -name "${pkg_pattern}*.tar.gz" | sort -V | tail -1)
  if [ -n "$pkg" ]; then
    log "INFO" "在当前目录找到安装包: $pkg"
    cp "$pkg" "${target_dir}/"
    return 0
  fi
  
  # 检查/tmp目录
  log "INFO" "在/tmp目录查找安装包..."
  pkg=$(find /tmp -maxdepth 1 -type f -name "${pkg_pattern}*.tar.xz" -o -name "${pkg_pattern}*.tar.gz" | sort -V | tail -1)
  if [ -n "$pkg" ]; then
    log "INFO" "在/tmp目录找到安装包: $pkg"
    cp "$pkg" "${target_dir}/"
    return 0
  fi
  
  log "WARN" "未找到匹配的本地安装包: ${pkg_pattern}*"
  return 1
}

# 从镜像站下载MySQL包
download_from_mirror() {
  local pkg_name=$1
  local target_dir=$2
  
  for url in "${PKG_URLS[@]}"; do
    log "INFO" "尝试从镜像站下载: ${url}${pkg_name}"
    if wget --no-check-certificate --tries=3 --timeout=30 -P "${target_dir}" "${url}${pkg_name}"; then
      log "INFO" "下载成功: ${url}${pkg_name}"
      return 0
    fi
    log "WARN" "下载失败: ${url}${pkg_name}"
  done
  
  return 1
}

# 下载MySQL
download_mysql() {
  local pkg_name=""
  local pkg_pattern=""
  
  # 获取glibc版本
  local glibc_ver=$(get_glibc_version | awk -F. '{print $1$2}')
  
  # 根据MySQL版本和glibc版本确定包名模式
  if [[ "$MYSQL_MAJOR" == "8.0" ]]; then
    pkg_pattern="mysql-${MYSQL_FULL}-linux-glibc${glibc_ver}-x86_64"
    pkg_name="${pkg_pattern}.tar.xz"
  else
    pkg_pattern="mysql-${MYSQL_FULL}-linux-glibc${glibc_ver}-x86_64"
    pkg_name="${pkg_pattern}.tar.gz"
      pkg_name="mysql-${MYSQL_FULL}-linux-glibc${ver}-x86_64.tar.gz"
      download_url="https://dev.mysql.com/get/Downloads/MySQL-${MYSQL_MAJOR}/${pkg_name}"
      
      echo "[INFO] 尝试下载: $pkg_name"
      if wget --no-check-certificate -P /tmp "$download_url" --tries=3 --timeout=30; then
        echo "[INFO] 下载成功: $pkg_name"
        return 0
      fi
      echo "[WARN] 下载失败: $pkg_name"
    done
  fi
  
  echo "[ERROR] 无法下载MySQL安装包，请检查网络或手动下载"
  return 1
    echo "[INFO] MySQL下载完成: $MYSQL_TAR"
}

extract_and_prepare() {
  mkdir -p $INSTALL_DIR $DATA_DIR
  tar -xf $MYSQL_TAR -C $INSTALL_DIR --strip-components=1
  useradd -r -s /sbin/nologin mysql || true
  chown -R mysql:mysql $INSTALL_DIR $DATA_DIR
}

init_mysql() {
  $INSTALL_DIR/bin/mysqld --initialize-insecure --user=mysql --basedir=$INSTALL_DIR --datadir=$DATA_DIR
}

gen_mycnf() {
  # 计算内存参数
  calculate_memory_params
  
  echo "[INFO] 生成 my.cnf 配置文件..."
  mkdir -p /etc/mysql/conf.d
  
  # 构建HugePage配置
  HUGEPAGE_CONFIG=""
  if [ $HUGEPAGE_PAGES -gt 0 ]; then
    HUGEPAGE_CONFIG="large-pages"
    # 计算HugePage大小（MB）
    HUGE_PAGE_SIZE_MB=$((HUGEPAGE_PAGES * 2))
  fi
  
  cat > /etc/my.cnf <<EOF
[mysqld]
# ===== 基础设置 =====
user = mysql
port = $PORT
basedir = $INSTALL_DIR
datadir = $DATA_DIR
socket = /tmp/mysql.sock
pid-file = $DATA_DIR/mysql.pid

# ===== 字符集 =====
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect='SET NAMES utf8mb4'

# ===== 网络 =====
bind-address = 0.0.0.0
max_connections = 2000
max_connect_errors = 1000
skip-name-resolve

# ===== 日志 =====
log-error = $DATA_DIR/error.log
slow_query_log = 1
slow_query_log_file = $DATA_DIR/slow.log
long_query_time = 2
log_queries_not_using_indexes = 1
log_timestamps = SYSTEM

# ===== InnoDB 设置 =====
innodb_buffer_pool_size = ${INNODB_BUFFER_POOL_SIZE}M
innodb_log_file_size = $(($INNODB_BUFFER_POOL_SIZE / 4 > 512 ? 512 : $INNODB_BUFFER_POOL_SIZE / 4))M
innodb_log_files_in_group = 2
innodb_log_buffer_size = 16M
innodb_flush_log_at_trx_commit = 1
innodb_flush_method = O_DIRECT
innodb_file_per_table = 1
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
innodb_read_io_threads = 8
innodb_write_io_threads = 8
innodb_purge_threads = 4
innodb_thread_concurrency = 0
innodb_buffer_pool_instances = $(($INNODB_BUFFER_POOL_SIZE > 8192 ? 8 : 4))
innodb_adaptive_hash_index = ON
innodb_change_buffering = all
innodb_flush_neighbors = 0
innodb_read_ahead_threshold = 0
innodb_adaptive_flushing = ON
innodb_stats_on_metadata = OFF
innodb_autoinc_lock_mode = 2

# ===== HugePage 配置 =====


# ===== 二进制日志 =====
server-id = 1
log-bin = $DATA_DIR/mysql-bin
binlog_format = ROW
binlog_row_image = FULL
expire_logs_days = 7
sync_binlog = 1
gtid_mode = ON
enforce_gtid_consistency = ON
binlog_group_commit_sync_delay = 100
binlog_group_commit_sync_no_delay_count = 10

# ===== 其他 =====
tmp_table_size = 64M
max_heap_table_size = 64M
table_open_cache = 4000
table_definition_cache = 4000
thread_cache_size = 16
open_files_limit = 65535
max_allowed_packet = 1G

# ===== 性能优化 =====
innodb_spin_wait_delay = 96
innodb_sync_spin_loops = 100
innodb_max_dirty_pages_pct = 90
innodb_max_dirty_pages_pct_lwm = 10
innodb_lru_scan_depth = 4000
innodb_checksum_algorithm = crc32
innodb_buffer_pool_load_at_startup = 1
innodb_buffer_pool_dump_at_shutdown = 1
innodb_buffer_pool_dump_pct = 25
innodb_page_cleaners = 4
innodb_undo_log_truncate = ON
#innodb_undo_logs = 128
innodb_max_undo_log_size = 1G
innodb_purge_rseg_truncate_frequency = 128

[client]
default-character-set = utf8mb4
socket = /tmp/mysql.sock

[mysql]
default-character-set = utf8mb4
prompt = "\\u@\\h [\\d]> "
no-auto-rehash

[mysqld_safe]
pid-file = $DATA_DIR/mysqld.pid
log-error = $DATA_DIR/error.log
open-files-limit = 65535

!includedir /etc/mysql/conf.d/
EOF
  
  chown -R mysql:mysql /etc/my.cnf /etc/mysql
  chmod 644 /etc/my.cnf
  
  # 创建错误日志文件并设置权限
  touch $DATA_DIR/error.log
  chown mysql:mysql $DATA_DIR/error.log
  chmod 640 $DATA_DIR/error.log
  
  echo "[INFO] my.cnf 配置文件已生成，InnoDB缓冲池大小: ${INNODB_BUFFER_POOL_SIZE}M"
  [ $HUGEPAGE_PAGES -gt 0 ] && echo "[INFO] 已启用 HugePage: ${HUGEPAGE_PAGES}页 (${HUGEPAGE_PAGES} * 2MB = $(($HUGEPAGE_PAGES * 2))MB)"
}

setup_environment() {
  echo "[INFO] 配置root用户环境变量..."
  # 创建MySQL环境变量配置文件
  cat > /etc/profile.d/mysql.sh <<EOF
# MySQL Environment Variables
export PATH=\$PATH:$INSTALL_DIR/bin

# 设置MySQL提示符
if [ "\$TERM " != "dumb" ]; then
    export MYSQL_PS1="\\u@\\h [\\d]> "
fi

# 添加MySQL客户端别名
alias mysql='mysql -h127.0.0.1 -P$PORT -uroot -p'
alias my='mysql -h127.0.0.1 -P$PORT -uroot -p'
alias mysqladmin='mysqladmin -h127.0.0.1 -P$PORT -uroot -p'
EOF

  # 使环境变量立即生效
  chmod +x /etc/profile.d/mysql.sh
  source /etc/profile.d/mysql.sh
  
  echo "[INFO] 已配置MySQL环境变量，请重新登录或执行 'source /etc/profile' 使配置生效"
}

setup_systemd() {
  echo "[INFO] 配置systemd服务..."
  cat > /etc/systemd/system/mysqld${PORT}.service <<EOF
[Unit]
Description=MySQL Server $PORT
After=network.target

[Service]
Type=forking
ExecStart=$INSTALL_DIR/bin/mysqld_safe --defaults-file=/etc/my.cnf --port=$PORT
User=mysql
Group=mysql
LimitNOFILE = 65536
Restart=on-failure
RestartPreventExitStatus=1
RestartSec=10
StartLimitInterval=300
StartLimitBurst=2
TimeoutStartSec=900
TimeoutStopSec=0


[Install]
WantedBy=multi-user.target
EOF

  systemctl daemon-reload
  systemctl enable mysqld${PORT}
  systemctl start mysqld${PORT}
  echo "[INFO] MySQL服务已配置并启动"
  
  # 设置环境变量
  setup_environment
}

set_root_pwd() {
  $INSTALL_DIR/bin/mysqladmin -uroot password "$MYSQL_ROOT_PASSWORD"
}

secure_mysql() {
  $INSTALL_DIR/bin/mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "DELETE FROM mysql.user WHERE User='';"
  $INSTALL_DIR/bin/mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "DROP DATABASE IF EXISTS test;"
  $INSTALL_DIR/bin/mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "FLUSH PRIVILEGES;"
}

output_info() {
  # 获取MySQL状态
  MYSQL_STATUS="未运行"
  if systemctl is-active mysqld &>/dev/null; then
    MYSQL_STATUS="运行中"
  fi
  
  # 获取HugePage信息
  HUGEPAGE_INFO="未启用"
  if [ $HUGEPAGE_PAGES -gt 0 ] && [ -f /proc/meminfo ]; then
    HUGE_TOTAL=$(grep '^HugePages_Total:' /proc/meminfo | awk '{print $2}')
    HUGE_FREE=$(grep '^HugePages_Free:' /proc/meminfo | awk '{print $2}')
    HUGE_SIZE=$(grep '^Hugepagesize:' /proc/meminfo | awk '{print $2}')
    HUGEPAGE_INFO="${HUGE_TOTAL}页 x ${HUGE_SIZE}KB = $((HUGE_TOTAL * HUGE_SIZE / 1024))MB (空闲: $((HUGE_FREE * HUGE_SIZE / 1024))MB)"
  fi
  
  # 获取THP状态
  THP_STATUS="未知"
  if [ -f /sys/kernel/mm/transparent_hugepage/enabled ]; then
    THP_STATUS=$(cat /sys/kernel/mm/transparent_hugepage/enabled | awk -F'[][]' '{print $2}')
  fi
  
  # 输出信息
  echo -e "\n\n[MySQL 安装完成]"
  echo "=================================================="
  echo "  MySQL 状态: $MYSQL_STATUS"
  echo "  安装目录: $INSTALL_DIR"
  echo "  数据目录: $DATA_DIR"
  echo "  配置文件: /etc/my.cnf"
  echo "  端口: $PORT"
  echo "  root 密码: $MYSQL_ROOT_PASSWORD"
  echo "  InnoDB缓冲池: ${INNODB_BUFFER_POOL_SIZE}M"
  echo -e "\n  透明大页(THP)状态: $THP_STATUS"
  echo "  HugePage 配置: $HUGEPAGE_INFO"
  echo "=================================================="
  echo -e "\n启动命令: systemctl start mysqld$PORT"
  echo "停止命令: systemctl stop mysqld$PORT"
  echo "重启命令: systemctl restart mysqld$PORT"
  echo "查看状态: systemctl status mysqld$PORT"
  echo "查看日志: journalctl -u mysqld$PORT -f"
  echo "设置开机自启: systemctl enable mysqld$PORT"
  echo -e "\n连接命令: mysql -u root -p'$MYSQL_ROOT_PASSWORD' -h 127.0.0.1 -P $PORT"
  echo -e "\n[安全建议]"
  echo "1. 请立即修改root密码: ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码';"
  echo "2. 删除测试数据库: DROP DATABASE IF EXISTS test;"
  echo "3. 删除匿名用户: DELETE FROM mysql.user WHERE user='';"
  echo "4. 创建专用应用用户并授权，避免使用root用户"
  echo -e "\n[性能调优建议]"
  echo "1. 根据实际负载情况调整innodb_buffer_pool_size参数"
  echo "2. 监控慢查询日志: $DATA_DIR/slow.log"
  echo "3. 定期优化表: mysqlcheck -u root -p --auto-repair --optimize --all-databases"
  echo -e "\n[注意事项]"
  echo "1. 请确保数据目录($DATA_DIR)有足够的空间"
  echo "2. 建议配置定期备份策略"
  echo "3. 生产环境建议配置主从复制或MGR高可用方案"
  echo -e "\n安装日志: $DATA_DIR/error.log\n"
}

# ========== 主流程 ==========
# 创建日志目录
mkdir -p "${LOG_DIR}"

# 记录脚本开始执行
log "INFO" "========== 开始执行MySQL安装脚本 =========="
log "INFO" "安装参数: MYSQL_MAJOR=${MYSQL_MAJOR}, MYSQL_FULL=${MYSQL_FULL}, PORT=${PORT}"
log "INFO" "安装目录: ${INSTALL_DIR}, 数据目录: ${DATA_DIR}"
log "INFO" "详细日志: ${LOG_FILE}"

# 主执行块
{
  # 0. 初始化日志重定向
  exec > >(tee -a "${LOG_FILE}") 2>&1
  
  # 1. 基础环境检查
  log "INFO" "1/8 开始基础环境检查..."
  if ! check_os; then
    log "ERROR" "操作系统检查失败，不支持的平台"
    exit 1
  fi
  check_hosts || log "WARN" "主机名检查失败，继续执行..."
  
  if ! check_port_usage; then
    log "ERROR" "端口检查失败，端口 ${PORT} 已被占用"
    exit 1
  fi
  
  if ! cleanup_target_dirs; then
    log "ERROR" "目录清理失败，请检查权限"
    exit 1
  }
  
  # 2. 配置软件源和安装基础包
  echo -e "\n[$(date '+%Y-%m-%d %H:%M:%S')] 2/8 配置软件源和安装基础包..."
  if ! config_yum_repo; then
    echo "[WARN] yum源配置失败，尝试继续执行..."
  fi
  install_common_packages || echo "[WARN] 部分基础包安装失败，继续执行..."
  
  # 3. 系统参数优化
  echo -e "\n[$(date '+%Y-%m-%d %H:%M:%S')] 3/8 系统参数优化..."
  close_firewalld_selinux || echo "[WARN] 防火墙/SELinux配置失败，继续执行..."
  check_limits || echo "[WARN] 系统限制检查失败，继续执行..."
  check_lang || echo "[WARN] 语言环境检查失败，继续执行..."
  set_swappiness || echo "[WARN] Swappiness设置失败，继续执行..."
  close_numa || echo "[WARN] NUMA配置失败，继续执行..."
  
  # 4. 内存优化配置
  echo -e "\n[$(date '+%Y-%m-%d %H:%M:%S')] 4/8 内存优化配置..."
  configure_thp || echo "[WARN] 透明大页配置失败，继续执行..."
  calculate_memory_params || { echo "[ERROR] 内存参数计算失败"; exit 1; }
  configure_hugepages || echo "[WARN] HugePage配置失败，继续执行..."
  
  # 5. 安装依赖和MySQL
  echo -e "\n[$(date '+%Y-%m-%d %H:%M:%S')] 5/8 安装依赖和MySQL..."
  install_deps || { echo "[ERROR] 依赖安装失败"; exit 1; }
  download_mysql || { echo "[ERROR] MySQL下载失败"; exit 1; }
  extract_and_prepare || { echo "[ERROR] MySQL解压准备失败"; exit 1; }
  init_mysql || { echo "[ERROR] MySQL初始化失败"; exit 1; }
  
  # 6. 配置MySQL
  echo -e "\n[$(date '+%Y-%m-%d %H:%M:%S')] 6/8 配置MySQL..."
  gen_mycnf || { echo "[ERROR] 生成my.cnf失败"; exit 1; }
  setup_systemd || { echo "[ERROR] 配置systemd服务失败"; exit 1; }
  
  # 7. 安全加固
  echo -e "\n[$(date '+%Y-%m-%d %H:%M:%S')] 7/8 安全加固..."
  set_root_pwd || { echo "[ERROR] 设置root密码失败"; exit 1; }
  secure_mysql || echo "[WARN] 安全加固步骤执行失败，继续执行..."
  
  # 8. 完成
  echo -e "\n[$(date '+%Y-%m-%d %H:%M:%S')] 8/8 安装完成!"
  
  # 输出安装信息
  output_info
  echo -e "\n[$(date '+%Y-%m-%d %H:%M:%S')] 安装日志已保存到: $LOG_FILE"
  
} || {
  echo -e "\n[ERROR] 安装过程中出现错误，请检查日志文件: $LOG_FILE"
  exit 1
}
